import json
import os.path

import redis
import smtplib
from sqlalchemy import func
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from flask import Flask, request, render_template, jsonify

from core.config import MAX_WORKERS
from utils import *
from core import config
from core.webparser.model import *

from apscheduler.schedulers.background import BackgroundScheduler

app = Flask(__name__)

# 数据库链接
redis = redis.Redis(host=config.REDIS_HOST, port=config.REDIS_PORT, db=config.REDIS_DB)

# 邮件发送
smtp_server = smtplib.SMTP_SSL(config.SMTP_SERVER, config.SMTP_PORT)
smtp_server.login(config.SMTP_SENDER_EMAIL, config.SMTP_PASSWORD)

# 常量定义
queue_id = 'WebLexGuard:queue-' + config.MARK_ID
already_urls_id = 'WebLexGuard:already-' + config.MARK_ID

# 用户配置文件读取（为什么写在 json 里？别问，问就是为了方便，为了完成任务。。。）
if not os.path.exists('./core/config.json'):
    with open('./core/config.json', 'w') as outfile:
        outfile.write(json.dumps({}))

# 任务调度器
scheduler = BackgroundScheduler()


def send_summary():
    from datetime import datetime

    with open('./core/config.json', 'r') as infile:
        task_config = json.load(infile)

    receiver_email = task_config.get('receiver_email')

    if not (receiver_email is not None and receiver_email.strip() != ''):
        return False, '请先设置接收者邮箱'

    # 获取数据
    data = get_queue_api()
    data['date'] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    # 创建邮件对象
    message = MIMEMultipart()
    message['From'] = config.SMTP_SENDER_EMAIL
    message['To'] = receiver_email
    message['Subject'] = '纠错报告'

    # 添加 HTML 内容
    message.attach(MIMEText(render_template('email.html', data=data), 'html'))

    smtp_server.sendmail(config.SMTP_SENDER_EMAIL, receiver_email, message.as_string())

    return True, '成功发送邮件'


@app.get('/queue')
def get_queue_api():
    """
    获取队列的所有信息
    """
    limit = request.args.get('limit', '5')
    page = request.args.get('page', '1')

    if limit.isdigit():
        limit = int(limit)
    else:
        return {
            'code': 1,
            'msg': '参数提供错误'
        }

    if page.isdigit():
        page = int(page)
    else:
        return {
            'code': 1,
            'msg': '参数提供错误'
        }

    limit = max(0, limit)
    page = max(1, page)
    offset = (page - 1) * limit

    # 队列中所有链接数、目前有问题的链接数
    queue_total = redis.llen(queue_id)
    already_total = redis.scard(already_urls_id)

    process_task = []

    # 获取所有正在处理的任务
    for i in range(MAX_WORKERS):
        task = redis.get('WebLexGuard:process-' + config.MARK_ID + ":" + str(i))

        if task is None:
            process_task.append({
                'url': '-',
                'code': 0,
                'status': '空闲等待任务中',
                'node': ''
            })
            continue

        process_task.append(dict(json.loads(task)))

    # 数据库
    session = db_session()

    result = []

    result_total = session.query(func.count(ProcessResultDB.id)).scalar()
    for recode in session.query(ProcessResultDB).offset(offset).limit(limit):
        result.append({
            'id': recode.id,
            'valid': recode.valid,
            'reason': recode.reason,
            'url': recode.url,
            'depth': recode.depth,
            'forward_urls': recode.forward_urls,
            'create_time': recode.create_time,
            'note': recode.note,
            'fix_info': recode.fix_info,
        })

    return {
        'code': 0,
        'already_total': already_total,
        'queue_total': queue_total,
        'process': process_task,
        'result': result,
        'result_total': result_total,
        'msg': '请求成功'
    }


@app.post('/queue')
def update_queue_api():
    url = request.form.get('url')

    if url.startswith('http://') or url.startswith('https://'):

        if url is None:
            return {
                'code': 1,
                'msg': '参数携带错误'
            }

        queue_lpush(redis, queue_id, url, [], 0)

        return {
            'code': 0,
            'msg': '提交队列成功'
        }

    return {
        'code': 1,
        'msg': '参数携带错误'
    }


@app.delete('/queue')
def delete_queue_api():
    try:
        redis.delete(queue_id)
    except Exception as e:
        return {
            'code': 1,
            'msg': '清空队列失败。' + str(e)
        }

    return {
        'code': 0,
        'msg': '清空队列成功'
    }


@app.get('/fixed')
def mark_fixed_api():
    id = request.args.get('id', None)

    if id is None:
        return {
            'code': 1,
            'msg': '参数携带错误'
        }

    session = db_session()
    fix_info = session.query(ProcessResultDB).where(ProcessResultDB.id == id).first().fix_info
    if fix_info == 'fixed':
        session.query(ProcessResultDB).where(ProcessResultDB.id == id).update({
            'fix_info': ''
        })
    else:
        session.query(ProcessResultDB).where(ProcessResultDB.id == id).update({
            'fix_info': 'fixed'
        })
    session.commit()

    return {
        'code': 0,
        'msg': '设置成功'
    }


@app.get('/clear')
def clear_api():
    # 获取集合中的所有元素
    all_members = redis.smembers(already_urls_id)

    # 遍历集合，删除不包含 "info" 的元素
    for member in all_members:
        if b"/info" not in member:  # 有 info 就是文章页的，偷懒这样写哈哈哈哈
            redis.srem(already_urls_id, member)  # 从集合中删除该元素
        elif str(member).endswith('https://www.jxnhu.edu.cn/'):  # 主页也顺便清理一下，偷懒了
            redis.srem(already_urls_id, member)

    return {
        'code': 0,
        'msg': '清理成功'
    }


@app.get('/config')
def config_api():
    with open('./core/config.json', 'r', encoding='utf-8') as f:
        return {
            'code': 0,
            'msg': '请求成功',
            'data': json.loads(f.read())
        }


@app.post('/config')
def config_post():
    setting = dict(request.form)

    setting['auto_index'] = setting['auto_index'] == 'on'
    setting['auto_cache_clear'] = setting['auto_cache_clear'] == 'on'

    with open('./core/config.json', 'w', encoding='utf-8') as f:
        f.write(json.dumps(setting, ensure_ascii=False))

    # 修改定时任务
    try:
        scheduler.remove_job('auto-task')
    except Exception as e:
        pass
    scheduler.add_job(auto_task, 'cron', hour=get_config().get('reminder_hour'),
                      minute=get_config().get('reminder_minute'), id='auto-task')

    return {
        'code': 0,
        'msg': '设置成功'
    }


@app.get('/')
def index_view():
    return render_template('index.html')


@app.get('/detail')
def detail_view():
    return render_template('detail.html')


@app.get('/send')
def send_view():
    success, msg = send_summary()
    return {
        'code': int(not success),
        'msg': msg
    }


def get_config():
    with open('./core/config.json', 'r', encoding='utf-8') as f:
        return json.loads(f.read())


def auto_task():
    with app.test_request_context('/'):
        # 清空缓存
        clear_api()
        # 添加主页
        queue_lpush(redis, queue_id, 'https://www.jxnhu.edu.cn/', [], 0)
        # 发送邮件
        send_view()

if get_config().get('reminder_hour') is not None:
    scheduler.add_job(auto_task, 'cron', hour=get_config().get('reminder_hour'),
                      minute=get_config().get('reminder_minute'), id='auto-task')
scheduler.start()

if __name__ == '__main__':
    app.run(host=config.HOST, port=config.PORT, debug=True)
